package com.logic.landseaserver.ws;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.logic.common.enums.HttpCodeEnum;
import com.logic.common.exception.BusinessException;
import com.logic.common.util.CommonResult;
import com.logic.common.util.CommonResultMap;
import com.logic.landseaserver.common.LandseaErrorMessageEnum;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.token.impl.WebBankQRTicketSerivce;
import com.logic.landseaserver.common.token.impl.WebBankTicketSerivce;
import com.logic.landseaserver.common.util.JsonObjectUtils;
import com.logic.landseaserver.common.util.LoggerHelper;
import com.logic.landseaserver.common.util.StringTools;
import com.logic.landseaserver.common.util.WeBankQRUtil;
import com.logic.landseaserver.service.IWeBankService;
import com.logic.landseaserver.ws.dto.CheckPayDTO;
import com.logic.landseaserver.ws.dto.WeBankPayCheckDTO;
import com.logic.landseaserver.ws.dto.WebankDataWapper;
import com.logic.landseaserver.ws.dto.WebankPayCallBackRequest;
import com.logic.landseaserver.ws.dto.WebankPayCallBackRequestWapper;
import com.logic.landseaserver.ws.dto.WebankQRPayCallBackRequest;
import com.logic.landseaserver.ws.dto.WebankQRPayCallBackRequestWapper;
import com.logic.landseaserver.ws.dto.WebankQRPaymentNotify;
import com.logic.landseaserver.ws.dto.WebankQRPaymentNotifyRequest;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

@Path("/webank")
@Api(value = "WeBank", description = "WeBank Rest WebServices", position = 42)
public class WeBankRest
{
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Autowired
    private IWeBankService weBankService;
    
    @Autowired
    private WebBankTicketSerivce webBankTicketSerivce;
    
    @Autowired
    private WebBankQRTicketSerivce webBankQRTicketSerivce;
    
    private static final Logger LOGGER = LoggerFactory.getLogger(WeBankRest.class);
    
    @POST
    @Path("/checkPayStatus")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "收单状态查询【 businessType: E0账单，E1预定 】")
    public Response checkPayStatus(@ApiParam(value = "param", required = true) CheckPayDTO payinfo)
    {
        CommonResultMap map = new CommonResultMap();
        try
        {
            WeBankPayCheckDTO paydto = weBankService.checkPay(payinfo);
            map.setData(paydto);
            map.setResult(CommonResult.SUCCESS);
        }
        catch (LandseaException e)
        {
            LoggerHelper.handleException(logger, "checkPayStatus", e, map);
        }
        catch (BusinessException e)
        {
            logger.error("checkPayStatus error.", e);
            map.setResult(CommonResult.BUSINESS_EXCEPTION, e.getMessage());
        }
        catch (Exception e)
        {
            logger.error("checkPayStatus error.", e);
            map.setResult(CommonResult.ERROR, e.getMessage());
        }
        return Response.status(HttpCodeEnum.OK.getCode()).entity(map).build();
    }
    
    @POST
    @Path("/callback")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "收银台支付成功回调函数", position = 1)
    public Response webankPayCallBack(
        @ApiParam(value = "paymentResponse", required = true) Map<String,Object> paymentResponse, @Context HttpServletRequest request)
    {
        LOGGER.info("微众支付结果通知|收银台|" + paymentResponse);
        
        CommonResultMap map = new CommonResultMap();
        try
        {
            WebankPayCallBackRequestWapper webankPayCallBackRequestWapper =  buildResponseFromRespMap(paymentResponse);
            String dataJsonString = StringTools.getString(paymentResponse.get("data"));
            checkSign(request, dataJsonString, webBankTicketSerivce.getTicket());
            
            List<WebankPayCallBackRequest> webankPayCallBackRequests =
                webankPayCallBackRequestWapper.getData().getTransResult();
            if (CollectionUtils.isEmpty(webankPayCallBackRequests))
            {
                // 数据问题需要重发
                map.setResult(CommonResult.BUSINESS_EXCEPTION);
                map.setMessage("transaction data is empty");
                return Response.status(HttpCodeEnum.SERVER_ERROR.getCode()).entity(map).build();
            }
            
            for (WebankPayCallBackRequest callBackRequest : webankPayCallBackRequests)
            {
                // 微众支付完以后 做一些操作
                if ("01".equals(callBackRequest.getTransStatus()))
                {
                    weBankService.weBankCallback(callBackRequest);
                    LOGGER.info("收银台支付成功！ -> OrderNo: " + ObjectUtils.toString(callBackRequest.getOrderNo()));
                    map.setResult(CommonResult.SUCCESS, "Pay Success");
                }
                else
                {
                    // 支付失败
                    LOGGER.info("收银台支付失败！ -> OrderNo: " + ObjectUtils.toString(callBackRequest.getOrderNo()));
                    map.setResult(CommonResult.SUCCESS, "Pay failed");
                }
            }
            
        }
        catch (LandseaException e)
        {
            LOGGER.info("收银台支付失败！ -> Exception: " + ObjectUtils.toString(e.getMessage()));
            map.setResult(CommonResult.BUSINESS_EXCEPTION, e.getMessage());
        }
        catch (Exception e)
        {
            LOGGER.info("收银台支付失败！-> Exception: " + ObjectUtils.toString(e.getMessage()));
            map.setResult(CommonResult.ERROR, e.getCause().getMessage());
        }
        
        return Response.status(HttpCodeEnum.OK.getCode()).entity(map).build();
    }
    
    @POST
    @Path("/QRcallback")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "收单支付成功回调函数", position = 1)
    public Response webankPayCallBackQR(
        @ApiParam(value = "paymentResponse", required = true) Map<String,Object> paymentResponse, @Context HttpServletRequest request)
    {
        LOGGER.info("微众支付结果通知|收单|" + paymentResponse);
        CommonResultMap map = new CommonResultMap();
        
        try
        {
            WebankQRPayCallBackRequestWapper webankQRPayCallBackRequestWapper =  buildResponseFromRespMapQR(paymentResponse);
            String dataJsonString = StringTools.getString(paymentResponse.get("data"));
            checkSign(request, dataJsonString, webBankQRTicketSerivce.getTicket());
            
            WebankQRPayCallBackRequest webankQRPayCallBackRequest = webankQRPayCallBackRequestWapper.getData();
            if (webankQRPayCallBackRequest == null)
            {
                // 数据问题需要重发
                map.setResult(CommonResult.BUSINESS_EXCEPTION);
                map.setMessage("transaction data is empty");
                return Response.status(HttpCodeEnum.SERVER_ERROR.getCode()).entity(map).build();
            }
            
            // 微众支付完以后 做一些操作
            if ("01".equals(webankQRPayCallBackRequest.getTradeStatus()))
            {
                weBankService.weBankQRCallback(webankQRPayCallBackRequestWapper);
                LOGGER.info("微众收单支付成功！ -> OrderNo: " + webankQRPayCallBackRequest.getOrderId());
                map.setResult(CommonResult.SUCCESS, "Pay Success");
            }
            else
            {
                // 支付失败
                LOGGER.info("微众收单支付失败！ -> OrderNo: " + webankQRPayCallBackRequest.getOrderId());
                map.setResult(CommonResult.SUCCESS, "Pay failed");
            }
        }
        catch (LandseaException e)
        {
            LoggerHelper.handleException(LOGGER, "webankPayCallBackQR", e, map);
        }
        catch (Exception e)
        {
            LOGGER.info("微众收单失败！ -> Exception: " + ObjectUtils.toString(e.getMessage()));
            map.setResult(CommonResult.ERROR, e.getCause().getMessage());
        }
        return Response.status(HttpCodeEnum.OK.getCode()).entity(map).build();
    }
    
    /**
     * 
     * [简要描述]：微众对账单回调</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-03)</br>
     *
     * @param callbackEvent
     * @return
     *
     */
    @POST
    @Path("/PaymentNotify")
    @Produces(MediaType.APPLICATION_JSON)
    @ApiOperation(value = "weBank reconciliation callback.")
    public Response weBankReconciliation(
        @ApiParam(value = "param", required = true) Map<String,Object> paymentResponse, @Context HttpServletRequest request)
    {
        LOGGER.info("weBankReconciliation|Request|" + paymentResponse);
        
        CommonResultMap map = new CommonResultMap();
        try
        {
            WebankQRPaymentNotify callbackEvent =  buildResponseFromReconciliation(paymentResponse);
            
            // 微众对账单不需要验证签名
            //String dataJsonString = StringTools.getString(paymentResponse.get("data"));
            //checkSign(request, dataJsonString, webBankQRTicketSerivce.getTicket());
            
            weBankService.weBankReconciliation(callbackEvent);
            map.setResult(CommonResult.SUCCESS);
        }
        catch (LandseaException e)
        {
            LoggerHelper.handleException(LOGGER, "weBankReconciliation", e, map);
        }
        return Response.status(HttpCodeEnum.OK.getCode()).entity(map).build();
    }
    
    private void checkSign(HttpServletRequest request, String dataString, String ticket) throws LandseaException
    {
        if (StringUtils.isBlank(ticket))
        {
            logger.error("checkSign|微众验签|本地获取ticket为空");
            throw LandseaException.createException(LandseaErrorMessageEnum.WEBANKQR_PARAM_TICKET_NULL);
        }
        
        String requestSign = request.getParameter("sign");
        String nonce_str = request.getParameter("nonce_str");
        String timestamp = request.getParameter("timestamp");
        
        List<String> signData = new ArrayList<String>();
        signData.add(nonce_str);
        signData.add(timestamp);
        signData.add(dataString);
        
        logger.info("checkSign|微众验签|signData|" + signData.toString());
        
        String signLocal = WeBankQRUtil.sign(signData, ticket);
        
        if (!requestSign.equalsIgnoreCase(signLocal))
        {
            logger.error("checkSign|微众验签失败|请求签名=" + requestSign + "|本地签名=" + signLocal);
            throw LandseaException.createException(LandseaErrorMessageEnum.WEBANKQR_SIGN_VALIDED_NULL);
        }
    }
    
    public WebankQRPayCallBackRequestWapper buildResponseFromRespMapQR(Map<String, Object> responseMap) throws LandseaException
    {
        WebankQRPayCallBackRequestWapper webankRequest = new WebankQRPayCallBackRequestWapper();
        
        webankRequest.setType(StringTools.getString(responseMap.get("type")));
        
        String dataJsonString = StringTools.getString(responseMap.get("data"));
        Map<String, Object> dataMap = JsonObjectUtils.jsonStr2map(dataJsonString);
        
        WebankQRPayCallBackRequest data = new WebankQRPayCallBackRequest();
        data.setBuyerAccount(StringTools.getString(dataMap.get("buyerAccount")));
        data.setBuyerId(StringTools.getString(dataMap.get("buyerId")));
        data.setChannelNo(StringTools.getString(dataMap.get("channelNo")));
        data.setIsSubscribe(StringTools.getString(dataMap.get("isSubscribe")));
        data.setOrderId(StringTools.getString(dataMap.get("orderId")));
        data.setOutTradeNo(StringTools.getString(dataMap.get("outTradeNo")));
        data.setPayTime(StringTools.getString(dataMap.get("payTime")));
        data.setTotalAmount(StringTools.getString(dataMap.get("totalAmount")));
        data.setTradeStatus(StringTools.getString(dataMap.get("tradeStatus")));
        
        webankRequest.setData(data);
        return webankRequest;
    }
    
    @SuppressWarnings({"rawtypes", "unchecked"})
    public WebankPayCallBackRequestWapper buildResponseFromRespMap(Map<String, Object> responseMap) throws LandseaException
    {
        WebankPayCallBackRequestWapper webankRequest = new WebankPayCallBackRequestWapper();
        
        webankRequest.setType(StringTools.getString(responseMap.get("type")));
        
        String dataJsonString = StringTools.getString(responseMap.get("data"));
        
        Map<String,Object> dataMap = JsonObjectUtils.jsonStr2map(dataJsonString);
        String transResult = StringTools.getString(dataMap.get("transResult"));
        
        List<Map> listData = JsonObjectUtils.jsonStr2ListBean(transResult, Map.class);
        
        List<WebankPayCallBackRequest> list = new ArrayList<WebankPayCallBackRequest>();
        for (Map<String, Object> map : listData)
        {
            WebankPayCallBackRequest req = new WebankPayCallBackRequest();
            req.setOrderNo(StringTools.getString(map.get("orderNo")));
            req.setReqDate(StringTools.getString(map.get("reqDate")));
            req.setReqTime(StringTools.getString(map.get("reqTime")));
            req.setTransStatus(StringTools.getString(map.get("transStatus")));
            req.setTransStatusDesc(StringTools.getString(map.get("transStatusDesc")));
            
            list.add(req);
        }
        
        WebankDataWapper webankDataWapper = new WebankDataWapper();
        webankDataWapper.setTransResult(list);
        
        webankRequest.setData(webankDataWapper);
        
        return webankRequest;
    }
    
    public WebankQRPaymentNotify buildResponseFromReconciliation(Map<String, Object> responseMap) throws LandseaException
    {
        WebankQRPaymentNotify webankRequest = new WebankQRPaymentNotify();
        
        webankRequest.setType(StringTools.getString(responseMap.get("type")));
        
        String dataJsonString = StringTools.getString(responseMap.get("data"));
        Map<String, Object> dataMap = JsonObjectUtils.jsonStr2map(dataJsonString);
        
        WebankQRPaymentNotifyRequest data = new WebankQRPaymentNotifyRequest();
        data.setFileHash(StringTools.getString(dataMap.get("file_hash")));
        data.setFileId(StringTools.getString(dataMap.get("file_id")));
        data.setToken(StringTools.getString(dataMap.get("token")));
        data.setWebankAppId(StringTools.getString(dataMap.get("webankAppId")));
        data.setWorkDate(StringTools.getString(dataMap.get("work_date")));
        
        webankRequest.setData(data);
        return webankRequest;
    }
}
